#include <stdio.h>
#include <string.h>

#include "SEGGER_RTT.h"

#include "FreeRTOS.h"
#include "task.h"

#include "base16.h"
#include "base64.h"
#include "encoding.h"

#include "drv_a9600.h"
#include "drv_bc26.h"
#include "drv_net_port.h"

#include "misc.h"

static struct _ENV_A9600 {
    uint32_t imxi;
}env_a9600;

void drv_a9600_cpin(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    BYTES res;
    MSG_AT msg_at;
    if (at_match(&bytes, "+s", &res)) {
        return;
    }
    msg_at.info = AT_INFO_CPIN;
    if (str_cmp((const char *)res.pdata, res.len, CSTRING("READY")) == 0) {
        msg_at.value = 0;
    } else {
        msg_at.value = 1;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

void drv_a9600_cops(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t mode;
    MSG_AT msg_at;
    if (at_match(&bytes, "+u", &mode)) {
        return;
    }
    msg_at.info = AT_INFO_COPS;
    if (mode == 2) {
        msg_at.value = 2;
    } else if (mode == 0 || mode == 1) {
        msg_at.value = 0;
    } else {
        return;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg %08x", msg_at.info);
        return;
    }
}

void drv_a9600_cme_error(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    uint32_t error_code;
    if (at_match(&bytes, "+u", &error_code)) {
        return;
    }
    if (error_code == 13) { //SIM failure
        msg_at.result = AT_RESULT_ERROR;
        if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
            NET_ERR("fail to pass the result");
        }
    }
}

void drv_a9600_csq(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t rssi;
    if (at_match(&bytes, "+u,+u", &rssi, NULL)) {
        return;
    }
    env_net.sq = rssi;
}

void drv_a9600_mipopen(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id, state;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u", &socket_id, &state)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket_id %d", socket_id);
        return;
    }
    msg_at.info = AT_INFO_MIPOPEN;
    msg_at.socket_inf.id = socket_id;
    if (state == 1) {
        msg_at.socket_inf.state = 1;
    } else if (state == 0) {
        msg_at.socket_inf.state = 0;
    } else {
        NET_ERR("invalid state %d", state);
        return;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
}
void drv_a9600_send_ready(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.result = AT_RESULT_OK;
    NET_INF("send ready");
    if (xQueueSend(env_net.qresult, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
}

void drv_a9600_mipsend(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u", &socket_id, NULL)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket_id %d", socket_id);
        return;
    }
    msg_at.info =  AT_INFO_MIPSEND;
    msg_at.socket_inf.id = socket_id;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
}


void drv_a9600_mipclose(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    uint32_t type;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u", &socket_id, &type)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket %d", socket_id);
        return;
    }
    msg_at.info = AT_INFO_MIPCLOSE;
    msg_at.socket_inf.id = socket_id;
    if (type == 0) {
        msg_at.socket_inf.state = 0;
    } else if (type == 1) {
        msg_at.socket_inf.state = 1;
    } else {
        NET_ERR("invalide socket close type %d", type);
        return;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the result");
    }
}

void drv_a9600_mipdata(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    uint32_t recv_en;
    MSG_RECV msg_recv;
    if (at_match(&bytes, "+d,+u,", &socket_id, &recv_en)) {
        return;
    }
    socket_id = socket_id - 1;
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket %d", socket_id);
        return;
    }
    if (BASE16_ENCODE_LEN(recv_en) != bytes.len) {
        NET_ERR("length doesn't match:%d,%d", BASE16_ENCODE_LEN(recv_en), bytes.len);
        return;
    }
    recv_en = base16_decode(bytes.pdata, bytes.len, bytes.pdata, bytes.len);
    if (recv_en == 0) {
        NET_ERR("failed to decode by base16");
        return;
    }
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = socket_id;
    app_fifo_write(&(env_net.pconns[socket_id].fifo), bytes.pdata, &recv_en);
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass recv");
    }
}

void drv_a9600_nnmi(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t recv_len;
    MSG_RECV msg_recv;
    if (at_match(&bytes, "+u,", &recv_len)) {
        return;
    }
    if (BASE16_ENCODE_LEN(recv_len) != bytes.len) {
        NET_ERR("length doesn't match:%d,%d", BASE16_ENCODE_LEN(recv_len), bytes.len);
        return;
    }	
	if(pdata[0] != '0' && pdata[1] != '1') {
		base16_decode(bytes.pdata,bytes.len,pdata,len);
		if(len == 0) {
			NET_ERR("failed to decode by base16");
			return;
		}
		NET_INF("decode len=%d",len);
//		app_fifo_write(&fifo_upgrade, pdata, &len);
//		msg_recv.type = MSG_RECV_UPGRADE;
	}  else {
    len = base64_base16_decode(bytes.pdata + 6, bytes.len - 6, pdata, len);
    if (len == 0) {
        NET_ERR("failed to decode by base64_base16");
        return;
    }
    app_fifo_write(&(env_net.pconns[0].fifo), pdata, &len);
    msg_recv.type = MSG_RECV_SOCKET;
}
    msg_recv.value = 0;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass socket msg");
        return;
    }
}

int drv_a9600_any(uint8_t *pdata, uint32_t len) {
    if (!str_all_number_pre((const char *)pdata, len)) { //imei or imsi?
        pdata[len] = '\0';
        if (env_a9600.imxi == 0) {
            if (len >= sizeof(env_net.pimei)) {
                NET_ERR("invalid imei:%s", pdata);
                return -1;
            }
            strcpy(env_net.pimei, (const char *)pdata);
//          NET_INF("imei:%s", pdata);
        } else {
            if (len >= sizeof(env_net.pimsi)) {
                NET_ERR("invalid imsi:%s", pdata);
                return -1;
            }
            strcpy(env_net.pimsi, (const char *)pdata);
            env_net.carrier = drv_net_find_carrier_by_imsi((const char *)pdata);
//          NET_INF("imsi:%s", pdata);
        }
        return 0;
    } else {
        return -1;
    }
}

void drv_a9600_cclk(uint8_t *pdata, uint32_t len) {
	NET_DATE_T t;
	BYTES bytes = { .pdata = pdata, .len = len };
	if (at_match(&bytes, "\"+u/+u/+u,+u:+u:+u",&t.year,&t.month,&t.days,&t.hours,&t.min,&t.sec)) {
		return;
	}
	t.year += 2000;
	drv_net_date_correct(&t, -8);
	NET_INF("date : %d/%d/%d : %d/%d/%d", t.year,t.month,t.days,t.hours,t.min,t.sec);
	memcpy(&env_net.date_t, &t,sizeof(NET_DATE_T));
}

extern void drv_bc26_miplcreate(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplevent(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplobserve(uint8_t *pdata, uint32_t len);
extern void drv_bc26_mipldiscover(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplwrite(uint8_t *pdata, uint32_t len);
extern void drv_bc26_miplread(uint8_t *pdata, uint32_t len);

AT_HANDLER drv_a9600_at_handlers[] = {
    AT_HANDLER_GENERAL_DEF("OK", drv_net_result_ok),
    AT_HANDLER_GENERAL_DEF("ERROR", drv_net_result_error),
    AT_HANDLER_GENERAL_DEF("+CSQ:", drv_a9600_csq),
    AT_HANDLER_GENERAL_DEF("+CME ERROR:", drv_a9600_cme_error),
    AT_HANDLER_GENERAL_DEF("+COPS:", drv_a9600_cops),
	AT_HANDLER_GENERAL_DEF("+CCLK:", drv_a9600_cclk),
    //tcp/udp
    AT_HANDLER_GENERAL_DEF("+MIPOPEN:", drv_a9600_mipopen),
    AT_HANDLER_GENERAL_DEF("+MIPSEND:", drv_a9600_mipsend),
    AT_HANDLER_GENERAL_DEF("> ", drv_a9600_send_ready),
    AT_HANDLER_GENERAL_DEF("+MIPCLOSE:", drv_a9600_mipclose),
    AT_HANDLER_GENERAL_DEF("+MIPDATA:", drv_a9600_mipdata),
//  AT_HANDLER_GENERAL_DEF("^RestoreUdpOK",ssss),
    //onenet
    AT_HANDLER_GENERAL_DEF("+MIPLCREATE", drv_bc26_miplcreate), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLEVENT", drv_bc26_miplevent), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLOBSERVE", drv_bc26_miplobserve), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLDISCOVER", drv_bc26_mipldiscover), //todo:
    AT_HANDLER_GENERAL_DEF("+MIPLWRITE:", drv_bc26_miplwrite),
    AT_HANDLER_GENERAL_DEF("+MIPLREAD", drv_bc26_miplread), //todo:
    //ocean connect
    AT_HANDLER_GENERAL_DEF("+NNMI:", drv_a9600_nnmi),
    AT_HANDLER_ANY(drv_a9600_any)
};



NET_ERR_CODE drv_a9600_cmd_sq_get(uint32_t *psq) {
    NET_ERR_CODE ret;
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSQ\r"));
    if (ret < 0) {
        *psq = 0;
        return ret;
    }
    *psq = env_net.sq;
    return NET_ERR_OK;
}

NET_ERR_CODE drv_a9600_date_info_get(NET_DATE_T *t) {
    NET_ERR_CODE ret;
    ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CCLK?\r"));
    if (ret < 0) {
        return NET_ERR_UNKOWN;
    }
	memcpy(t, &env_net.date_t,sizeof(NET_DATE_T));
    return NET_ERR_OK;	
}


NET_ERR_CODE drv_a9600_executor(NET_CMD_CODE cmd, void *pdata) {
    if (cmd == NET_CMD_SQ_GET) {
        return drv_a9600_cmd_sq_get((uint32_t *)pdata);
    } else if (cmd == NET_CMD_CT_ID_TYPE_GET){
        *((NET_CT_ID_TYPE *)pdata) = NET_CT_ID_IMEI;
        return NET_ERR_OK;
	} else if (cmd == NET_CMD_CCLK_TIME_GET) {
		return drv_a9600_date_info_get((NET_DATE_T *)pdata);
    } else {
        return NET_ERR_UNKOWN;
    }
}

NET_ERR_CODE drv_a9600_try_to_get_ip(void) {
    NET_ERR_CODE ret;
    uint32_t i;
    MSG_AT msg_at;

    //close echo
    ret = drv_net_write_bytes_raw(1000, 2, TSTRING(uint8_t * , "ATE0\r"));
    if (ret < 0) {
        NET_ERR("failed to close echo");
        return NET_ERR_UNKOWN;
    }

    //read imsi
    env_a9600.imxi = 0xFFFFFFFF;
    ret = drv_net_write_bytes_raw(5000, 2, TSTRING(uint8_t * , "AT+CIMI\r"));
    if (ret < 0) {
        if (ret == NET_ERR_AT) {
            NET_ERR("no sim card");
            return NET_ERR_NO_CARD;
        } else {
            return NET_ERR_UNKOWN;
        }
    }
	env_net.sim_status = 1;
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CPSMS=0\r"));
    if (ret < 0) {
        NET_ERR("failed to disable the psm");
        return ret;
    }
    //read imei
    env_a9600.imxi = 0;
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CGSN\r"));
    if (ret < 0) {
        NET_ERR("failed to read imei");
        return NET_ERR_UNKOWN;
    }
    //now sim card is ready,let's check status of registration
    for (i = 30; i > 0; i--) {
        NET_INF("try to get ip%d...", i);
        drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSQ\r"));
        xQueueReset(env_net.qinfo);
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+COPS?\r"));
        if (ret < 0) {
            NET_ERR("failed to query the current operator");
            return NET_ERR_UNKOWN;
        }
        if (drv_net_msg_at_receive(AT_INFO_COPS, 1, 1000, &msg_at, NULL) == NET_ERR_OK) {
            if (msg_at.value == 0) {
                NET_INF("registered successfully");
                break;
            }
            vTaskDelay(1000);
        }
        if (i == 1) { //last loop
            NET_ERR("registration timed out");
            return NET_ERR_UNKOWN;
        }
    }
    //set psm
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CPSMS=1,,,\"01000100\",\"00000001\"\r"));
    if (ret < 0) {
        NET_ERR("failed to enable the psm");
        return ret;
    }
	ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CGMR\r"));
	ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CGDCONT=?\r"));
    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CPSMS?\r"));
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+NVSETPM=2\r"));
    if (ret < 0) {
        NET_ERR("failed to set power mode");
        return ret;
    }
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSCLK=2\r"));
    if (ret < 0) {
        NET_ERR("failed to set clock");
        return ret;
    }
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT&W\r"));
    if (ret < 0) {
        NET_ERR("failed to save configuration");
        return ret;
    }
    //set net data format
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+MIPMODE=1,0,0\r"));
    if (ret < 0) {
        NET_ERR("failed to set data format");
        return ret;
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_a9600_try_to_sleep(void) {
    uint32_t i;
    NET_INT_ENABLE();
    for (i = 60; i > 0; i--) {
        if (drv_net_write_bytes_raw(700, 2, TSTRING(uint8_t * , "AT\r")) != NET_ERR_OK) {
            NET_INF("enter psm successfully");
            return NET_ERR_OK;
        } else {
            vTaskDelay(1000);
        }
        NET_INF("try to sleep%d...",i);
        vTaskDelay(1000);
    }
//  if (drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT\r")) != NET_ERR_OK) {
//      NET_INF("module is sleeping");
//      return NET_ERR_OK;
//  }
    NET_ERR("failed to enter psm");
    return NET_ERR_UNKOWN;
}

NET_ERR_CODE drv_a9600_try_to_wake_up(void) {
    uint32_t i;
    NET_INF("wake up...");
    for (i = 6; i > 0; i--) {
        drv_net_power_wake_up();
        if (drv_net_write_bytes_raw(500, 2, TSTRING(uint8_t * , "AT\r")) == NET_ERR_OK) {
            break;
        } else if (i == 1) {
            NET_ERR("failed to wake up module");
            return NET_ERR_UNKOWN;
        }
    }
    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+MIPOPEN?\r"));
    return NET_ERR_OK;
}

NET_ERR_CODE drv_a9600_power_set(NET_POWER_CONTROL net_power) {
    if (net_power == NET_POWER_ON) {
        env_net.pat_handlers = drv_a9600_at_handlers;
        return drv_a9600_try_to_get_ip();
    } else if (net_power == NET_POWER_OFF) {
        drv_net_power_off();
        return NET_ERR_OK;
    } else if (net_power == NET_POWER_SLEEP) {
        return drv_a9600_try_to_sleep();
    } else if (net_power == NET_POWER_WAKE) {
        return drv_a9600_try_to_wake_up();
    } else {
        NET_ERR("invalid power state");
        return NET_ERR_UNKOWN;
    }
}


static NET_ERR_CODE drv_a9600_socket_connect(int32_t socket,
                                             DRV_NET_SOCKET_TYPE service_type,
                                             char *pstr_ip,
                                             char *pstr_port,
                                             uint32_t fifo_size) {
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    char pbuf[64];
    int32_t len;
    uint32_t i;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket");
        return NET_ERR_UNKOWN;
    }
    if ((service_type != DRV_NET_SOCKET_TCP) && (service_type != DRV_NET_SOCKET_UDP)) {
        NET_ERR("invalid service type%d", service_type);
        return NET_ERR_UNKOWN;
    }
    if ((pstr_ip == NULL) || (pstr_port == NULL)) {
        NET_ERR("no ip or port");
        return NET_ERR_UNKOWN;
    }
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[socket].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }
    //AT+MIPOPEN=<Socket_ID>,<Protocol>,<Remote_IP>,<Remote_Port>[,Local_Port]
    //AT+MIPOPEN=1,"UDP","59.174.243.135",161,7000
    if (service_type == DRV_NET_SOCKET_TCP) {
        len = snprintf(pbuf, sizeof(pbuf), "AT+MIPOPEN=%d,\"TCP\",\"%s\",%s\r", socket + 1, pstr_ip, pstr_port);
    } else {
        len = snprintf(pbuf, sizeof(pbuf), "AT+MIPOPEN=%d,\"UDP\",\"%s\",%s\r", socket + 1, pstr_ip, pstr_port);
    }
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(2000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to connect");
        return ret;
    }
    for (i = 40; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_MIPOPEN) {
                if (msg_at.socket_inf.id == socket) {
                    if (msg_at.socket_inf.state == 1) {
                        NET_INF("connected successfully");
                        break;
                    } else {
                        NET_ERR("socket was closed");
                        return NET_ERR_UNKOWN;
                    }
                } else {
                    NET_WAR("unexpected socket %d", socket);
                }
            } else {
                NET_WAR("unexpected inf %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("connection timed out");
            return NET_ERR_UNKOWN;
        }
        NET_INF("msg%d", i);
    }
    return NET_ERR_OK;
}


static NET_ERR_CODE drv_a9600_socket_send(int32_t socket, uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    uint32_t i;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    if (pdata == NULL) {
        NET_ERR("null pointer");
        return NET_ERR_UNKOWN;
    }
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket %d", socket);
        return NET_ERR_UNKOWN;
    }
    pbuf = pvPortMalloc(BASE16_ENCODE_LEN(len) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to malloc send buffer");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+MIPSEND=%d,%d\r", socket + 1, len);
    if (pbuf_len < 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }

    ret = drv_net_write_bytes_raw(1000, 1, pbuf, pbuf_len);
    if (ret < 0) {
        NET_ERR("transmission is not ready");
        vPortFree(pbuf);
        return ret;
    }

    pbuf_len = base16_encode(pdata, len, pbuf, BASE16_ENCODE_LEN(len));
    if (pbuf_len == 0) {
        NET_ERR("failed to encode data by base16");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    pbuf[pbuf_len] = 0x1A; //ctrl+z
    pbuf_len += 1;

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(10000, 1, pbuf, pbuf_len);

    vPortFree(pbuf);

    if (ret < 0) {
        NET_ERR("failed to send");
        return ret;
    }
    for (i = 20; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_MIPSEND) {
                if (msg_at.socket_inf.id == socket) {
//                  NET_INF("send successfully");
                    break;
                } else {
                    NET_WAR("unexpected socket %d", msg_at.socket_inf.id);
                }
            } else {
                NET_WAR("unexpected info %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("transmission timed out");
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_OK;
}


static NET_ERR_CODE drv_a9600_socket_disconnect(int32_t socket) {
    char pbuf[32];
    int32_t len;
    uint32_t i;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket:%d", socket);
        return NET_ERR_UNKOWN;
    }
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPCLOSE=%d\r", socket + 1);
    if (len <= 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }

    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(10000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to close the socket %d", socket);
        return ret;
    }

    for (i = 20; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_MIPCLOSE) {
                if (msg_at.socket_inf.id == socket) {
                    if (msg_at.socket_inf.state == 0) {
                        NET_INF("closed socket %d successfully", socket);
                        break;
                    } else {
                        NET_ERR("close socket %d", socket);
                        return NET_ERR_UNKOWN;
                    }
                } else {
                    NET_WAR("unexpect socket %d", msg_at.socket_inf.id);
                }
            } else {
                NET_WAR("unexpect info %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("timeout occurs at closing socket");
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_OK;
}


//use imei
static NET_ERR_CODE drv_a9600_ct_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    NET_ERR_CODE ret;
	char buffer[64];
	int32_t len;
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[0].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("failed to init fifo");
        return ret;
    }

    //set receive mode
    ret = drv_net_write_bytes_raw(2000, 1, TSTRING(uint8_t * , "AT+NNMI=1\r"));
    if (ret < 0) {
        NET_ERR("failed to set receive mode");
        return ret;
    }
	len = snprintf(buffer, sizeof(buffer), "AT+NCDPOPEN=\"%s\",%s\r", pstr_ip, pstr_port);
    ret = drv_net_write_bytes_raw(20000, 1,(uint8_t *)buffer, len);
    if (ret < 0) {
        NET_ERR("failed to connect to ocean connect");
        return ret;
    }

    return NET_ERR_OK;
}

static NET_ERR_CODE drv_a9600_ct_send(uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    NET_ERR_CODE ret;
    pbuf = pvPortMalloc(BASE16_ENCODE_LEN(BASE64_ENCODE_LEN(len)) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+NMGS=%d,00%02X%02x", BASE64_ENCODE_LEN(len) + 3, BASE64_ENCODE_LEN(len) >> 8,BASE64_ENCODE_LEN(len) & 0xFFu);
    if (pbuf_len <= 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base16_base64_encode(pdata, len, pbuf + pbuf_len, BASE16_ENCODE_LEN(BASE64_ENCODE_LEN(len)));
    if (len == 0) {
        NET_ERR("failed to encode by base16_base64");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    pbuf_len += len;

    memcpy(pbuf + pbuf_len, CSTRING("\r"));
    pbuf_len += (sizeof("\r") - 1);

    ret = drv_net_write_bytes_raw(1000, 2, pbuf, pbuf_len);

    vPortFree(pbuf);
    if (ret < 0) {
        NET_ERR("failed to send");
        return ret;
    }
    return NET_ERR_OK;
}
NET_ERR_CODE drv_a9600_upgrade_send(uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    NET_ERR_CODE ret;
    pbuf = pvPortMalloc(BASE16_ENCODE_LEN(len) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    pbuf_len = snprintf((char *)pbuf, 64, "AT+NMGS=%d,00%02X%02x",len + 3, len >> 8,len & 0xFFu);
    if (pbuf_len <= 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base16_encode(pdata, len, pbuf + pbuf_len, BASE16_ENCODE_LEN(len));
    if (len == 0) {
        NET_ERR("failed to encode by base16_base64");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    pbuf_len += len;

    memcpy(pbuf + pbuf_len, CSTRING("\r"));
    pbuf_len += (sizeof("\r") - 1);

    ret = drv_net_write_bytes_raw(1000, 2, pbuf, pbuf_len);

    vPortFree(pbuf);
    if (ret < 0) {
        NET_ERR("failed to send");
        return ret;
    }
    return NET_ERR_OK;
}
static NET_ERR_CODE drv_a9600_ct_disconnect(void) {
    NET_ERR_CODE ret;
    ret = drv_net_write_bytes_raw(2000, 1, TSTRING(uint8_t * , "AT+NCDPCLOSE\r"));
    if (ret < 0) {
        NET_ERR("failed to disconnect with oncean connect");
        return ret;
    }
    return NET_ERR_OK;
}

static NET_ERR_CODE drv_a9600_cm_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    return drv_net_cm_connect(NULL, NULL, fifo_size, CM_FLAG_LIFETIME);
}

static NET_ERR_CODE drv_a9600_cm_send(uint8_t *pdata, uint32_t len) {
    return drv_net_cm_send(pdata, len, 0);
}

NET_ERR_CODE drv_a9600_cm_disconnect(void) {
    return drv_net_cm_disconnect();
}

const DRV_NET_INTERFACE drv_a9600_interface = {
    .pf_power_set = drv_a9600_power_set,
    .pf_executor = drv_a9600_executor,
    //ct
    .pf_ct_connect = drv_a9600_ct_connect,
    .pf_ct_send = drv_a9600_ct_send,
    .pf_ct_disconnect = drv_a9600_ct_disconnect,
    //cm
    .pf_cm_connect = drv_a9600_cm_connect,
    .pf_cm_send = drv_a9600_cm_send,
    .pf_cm_disconnect = drv_a9600_cm_disconnect,
    //socket
    .pf_socket_connect = drv_a9600_socket_connect,
    .pf_socket_send = drv_a9600_socket_send,
    .pf_socket_disconnect = drv_a9600_socket_disconnect
};
